package com.example.mywmb

import android.util.Log
import java.io.FileOutputStream
import java.io.IOException
import java.util.ArrayList
import java.util.Collections

/**
 * 继电器控制器
 * 直接通过串口控制Modbus继电器模块
 */
class RelayController {
    
    private val TAG = "RelayController"
    private val SERIAL_PORT = "/dev/ttyS1"
    
    // 备用串口路径列表
    private val BACKUP_SERIAL_PATHS = arrayOf(
        "/dev/ttyS0",
        "/dev/ttyUSB0",
        "/dev/ttyUSB1",
        "/dev/ttyACM0",
        "/dev/ttyACM1"
    )
    
    companion object {
        // Modbus RTU协议常量
        private const val SLAVE_ADDRESS = 0xFF.toByte()        // 从机地址 (255)
        private const val WRITE_SINGLE_COIL = 0x05.toByte()    // 写单个线圈
        private const val WRITE_MULTIPLE_COILS = 0x0F.toByte() // 写多个线圈
        private const val READ_COILS = 0x01.toByte()           // 读线圈状态
        private const val RELAY_COUNT = 8                       // 继电器数量
    }
    
    /**
     * 打开所有继电器
     */
    fun openAllRelays() {
        try {
            Log.d(TAG, "开启所有继电器...")
            
            // 创建开启所有继电器的Modbus命令
            val command = createAllRelaysCommand(true)
            
            // 发送命令
            sendCommand(command)
            
            Log.d(TAG, "所有继电器开启命令已发送")
        } catch (e: Exception) {
            Log.e(TAG, "开启继电器失败: ${e.message}")
            throw e
        }
    }
    
    /**
     * 关闭所有继电器
     */
    fun closeAllRelays() {
        try {
            Log.d(TAG, "关闭所有继电器...")
            
            // 创建关闭所有继电器的Modbus命令
            val command = createAllRelaysCommand(false)
            
            // 发送命令
            sendCommand(command)
            
            Log.d(TAG, "所有继电器关闭命令已发送")
        } catch (e: Exception) {
            Log.e(TAG, "关闭继电器失败: ${e.message}")
            throw e
        }
    }
    
    /**
     * 控制单个继电器
     * @param relayNumber 继电器编号 (1-8)
     * @param isOpen 是否打开继电器
     */
    fun controlRelay(relayNumber: Int, isOpen: Boolean) {
        try {
            Log.d(TAG, "${if (isOpen) "打开" else "关闭"}继电器 $relayNumber...")
            
            // 继电器编号从0开始，所以需要减1
            val relayAddress = relayNumber - 1
            
            // 创建控制单个继电器的Modbus命令
            val command = byteArrayOf(
                SLAVE_ADDRESS,
                WRITE_SINGLE_COIL,
                (relayAddress shr 8).toByte(), // 高8位地址
                (relayAddress and 0xFF).toByte(), // 低8位地址
                if (isOpen) 0xFF.toByte() else 0x00.toByte(), // 开启为0xFF，关闭为0x00
                0x00.toByte() // 占位
            )
            
            // 计算CRC16校验码
            val crc = calculateCRC16(command)
            val crcLow = (crc and 0xFF).toByte()
            val crcHigh = ((crc shr 8) and 0xFF).toByte()
            
            val fullCommand = command + crcLow + crcHigh
            
            // 发送命令
            sendCommand(fullCommand)
            
            Log.d(TAG, "继电器 $relayNumber ${if (isOpen) "打开" else "关闭"}命令已发送")
        } catch (e: Exception) {
            Log.e(TAG, "${if (isOpen) "打开" else "关闭"}继电器 $relayNumber 失败: ${e.message}")
            throw e
        }
    }
    
    /**
     * 控制多个继电器
     * @param relayNumbers 需要控制的继电器编号列表 (1-8)
     * @param isOpen 是否打开继电器
     */
    fun controlMultipleRelays(relayNumbers: List<Int>, isOpen: Boolean) {
        relayNumbers.forEach { relayNumber ->
            controlRelay(relayNumber, isOpen)
        }
    }
    
    /**
     * 创建控制所有继电器的命令
     */
    private fun createAllRelaysCommand(isOpen: Boolean): ByteArray {
        val command = byteArrayOf(
            SLAVE_ADDRESS,
            WRITE_MULTIPLE_COILS,
            0x00, 0x00,  // 起始地址
            0x00, RELAY_COUNT.toByte(),  // 继电器数量
            0x01  // 字节数
        )
        
        // 设置数据字节
        // 这里我们假设所有继电器都连接到同一组
        // 0xFF表示所有继电器开启，0x00表示所有继电器关闭
        val dataByte = if (isOpen) 0xFF.toByte() else 0x00.toByte()
        val fullCommand = command + dataByte
        
        // 计算CRC16校验码
        val crc = calculateCRC16(fullCommand)
        val crcLow = (crc and 0xFF).toByte()
        val crcHigh = ((crc shr 8) and 0xFF).toByte()
        
        return fullCommand + crcLow + crcHigh
    }
    
    /**
     * 发送命令到串口
     */
    private fun sendCommand(command: ByteArray) {
        // 先尝试默认串口
        if (trySendCommandToPort(SERIAL_PORT, command)) {
            return
        }
        
        // 如果默认串口失败，尝试备用串口
        for (portPath in BACKUP_SERIAL_PATHS) {
            if (portPath == SERIAL_PORT) continue // 跳过已尝试的主串口
            
            if (trySendCommandToPort(portPath, command)) {
                Log.d(TAG, "成功通过备用串口 $portPath 发送命令")
                return
            }
        }
        
        // 所有串口都失败
        throw IOException("串口都无法发送命令")
    }
    
    /**
     * 尝试通过指定串口发送命令
     */
    private fun trySendCommandToPort(portPath: String, command: ByteArray): Boolean {
        return try {
            Log.d(TAG, "尝试发送命令到串口: $portPath")
            
            // 检查串口设备是否存在
            val serialFile = java.io.File(portPath)
            if (!serialFile.exists()) {
                Log.d(TAG, "串口设备不存在: $portPath")
                return false
            }
            
            // 配置串口参数
            val configCommand = "stty -F $portPath 9600 cs8 -cstopb -parenb raw -echo"
            val configProcess = Runtime.getRuntime().exec(configCommand)
            val configResult = configProcess.waitFor()
            
            if (configResult != 0) {
                Log.w(TAG, "串口配置失败，但继续尝试发送数据: $portPath")
            }
            
            // 发送数据
            FileOutputStream(serialFile).use { fos ->
                fos.write(command)
                fos.flush()
                Log.d(TAG, "命令发送成功到 $portPath: ${command.joinToString(" ") { "%02X".format(it) }}")
            }
            
            return true
        } catch (e: IOException) {
            Log.d(TAG, "发送命令到 $portPath 失败: ${e.message}")
            return false
        } catch (e: Exception) {
            Log.d(TAG, "发送命令到 $portPath 时发生未知错误: ${e.message}")
            return false
        }
    }
    
    /**
     * 计算CRC16校验码
     */
    private fun calculateCRC16(data: ByteArray): Int {
        var crc = 0xFFFF
        
        for (byte in data) {
            crc = crc xor (byte.toInt() and 0xFF)
            
            for (i in 0 until 8) {
                if (crc and 0x0001 != 0) {
                    crc = (crc shr 1) xor 0xA001
                } else {
                    crc = crc shr 1
                }
            }
        }
        
        return crc
    }
    
    /**
     * 测试串口连接
     */
    fun testConnection(): Boolean {
        return tryConnection(SERIAL_PORT) || tryBackupPorts()
    }
    
    /**
     * 尝试连接指定的串口
     */
    private fun tryConnection(portPath: String): Boolean {
        return try {
            val serialFile = java.io.File(portPath)
            val exists = serialFile.exists()
            val canWrite = if (exists) {
                try {
                    serialFile.canWrite()
                } catch (e: Exception) {
                    Log.d(TAG, "无法检查写权限: ${e.message}")
                    true // 假设可以写
                }
            } else false
            
            Log.d(TAG, "串口测试 $portPath - 存在: $exists, 可写: $canWrite")
            exists && canWrite
        } catch (e: Exception) {
            Log.e(TAG, "串口连接测试失败 $portPath: ${e.message}")
            false
        }
    }
    
    /**
     * 尝试备用串口路径
     */
    private fun tryBackupPorts(): Boolean {
        for (portPath in BACKUP_SERIAL_PATHS) {
            if (portPath == SERIAL_PORT) continue // 跳过主串口
            
            if (tryConnection(portPath)) {
                Log.d(TAG, "备用串口 $portPath 连接成功")
                return true
            }
        }
        Log.d(TAG, "所有备用串口连接失败")
        return false
    }
    
    /**
     * 获取当前串口路径
     */
    fun getCurrentSerialPort(): String {
        return SERIAL_PORT
    }
    
    /**
     * 检查root权限
     */
    fun checkRootPermission(): Boolean {
        return try {
            val process = Runtime.getRuntime().exec("su -c 'echo test'")
            val exitCode = process.waitFor()
            val hasRoot = exitCode == 0
            Log.d(TAG, "Root权限检查: $hasRoot")
            hasRoot
        } catch (e: Exception) {
            Log.e(TAG, "Root权限检查失败: ${e.message}")
            false
        }
    }
    
    /**
     * 发送测试命令
     */
    fun sendTestCommand() {
        try {
            Log.d(TAG, "发送测试命令...")
            
            // 创建读取线圈状态命令
            val command = byteArrayOf(
                SLAVE_ADDRESS,
                READ_COILS,
                0x00, 0x00,  // 起始地址
                0x00, 0x08   // 线圈数量
            )
            
            val crc = calculateCRC16(command)
            val fullCommand = command + byteArrayOf(
                (crc and 0xFF).toByte(),
                ((crc shr 8) and 0xFF).toByte()
            )
            
            sendCommand(fullCommand)
            Log.d(TAG, "测试命令已发送")
        } catch (e: Exception) {
            Log.e(TAG, "发送测试命令失败: ${e.message}")
        }
    }
}